﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Istrib.Sound;
using Istrib.Sound.Formats;

namespace SpeechRecognizer
{
	public class SoundIO : IDisposable
	{
		#region Private Fields
		private Mp3SoundCapture _mp3SoundCapture;
		#endregion

		#region Public Fields
		public SoundCaptureDevice CaptureDevice
		{
		    get { return _mp3SoundCapture.CaptureDevice; }
		    set
		    {
		        _mp3SoundCapture.CaptureDevice = value;
		    }
		}

		public Mp3SoundCapture.Outputs OutputType
		{
		    get { return _mp3SoundCapture.OutputType; }
		    set { _mp3SoundCapture.OutputType = value; }
		}

		public PcmSoundFormat SoundFormat
		{
		    get { return _mp3SoundCapture.WaveFormat; }
		    set { _mp3SoundCapture.WaveFormat = value; }
		}

		/// <summary>
		/// If true record stream will be closed after all the date is written.
		/// If false record stream will be closed immediately. The data loss is possible.
		/// </summary>
		public bool WaitOnStop
		{
			get { return _mp3SoundCapture.WaitOnStop; }
			set { _mp3SoundCapture.WaitOnStop = value; }
		}

		/// <summary>
		/// If true volume level would be normilized
		/// </summary>
		public bool NormalizeVolume
		{
			get { return _mp3SoundCapture.NormalizeVolume; }
			set { _mp3SoundCapture.NormalizeVolume = value; }
		}

		/// <summary>
		/// Use this to subscribe on events
		/// </summary>
		public Mp3SoundCapture SoundCaptureEngine
		{
			get { return _mp3SoundCapture; }
		}

		public Stream OutputStream { get; set; }

		public String OutputFilePath { get; set; }

		private static IEnumerable<SoundCaptureDevice> AvailableDevices
		{ get { return SoundCaptureDevice.AllAvailable; } }

		private static IEnumerable<PcmSoundFormat> StandartSoundFormats
		{ get { return PcmSoundFormat.StandardFormats.Where(d => d.BitsPerSample >= 16); } }

		public static IEnumerable<String> AvailableDevicesDescriptions
		{ get { return SoundCaptureDevice.AllAvailable.Select(d => d.Description); } }

		public static IEnumerable<String> StandartSoundFormatsDescriptions
		{ get { return PcmSoundFormat.StandardFormats.Where(d => d.BitsPerSample >= 16).Select(d => d.Description); } }
		#endregion

		public SoundIO(SoundCaptureDevice captureDevice, Mp3SoundCapture.Outputs outputType, PcmSoundFormat soundFormat)
		{
			_mp3SoundCapture = new Mp3SoundCapture
								{
									CaptureDevice = captureDevice,
									OutputType = outputType,
									WaveFormat = soundFormat,
									WaitOnStop = true,
									NormalizeVolume = true
								};
		}

		/// <summary>
		/// That's architecture "feature" is used in order to have fewer references in a project that actualy uses the SpeechRecognizer.
		/// Unfortunately, a bunch of dlls is still required.
		/// </summary>
		/// <param name="captureDeviceDesc"></param>
		/// <param name="outputTypeDesc"></param>
		/// <param name="soundFormatDesc"></param>
		/// <returns></returns>
		public static SoundIO  getSoundIO(String captureDeviceDesc, String outputTypeDesc, String soundFormatDesc)
		{
			Mp3SoundCapture.Outputs outputType;
			switch (outputTypeDesc.ToLower())
			{
				case"wav": outputType = Mp3SoundCapture.Outputs.Wav; break;
				case "mp3": outputType = Mp3SoundCapture.Outputs.Mp3; break;
				case "rawpcm": outputType = Mp3SoundCapture.Outputs.RawPcm; break;
				default:
					outputType = Mp3SoundCapture.Outputs.Wav;
					break;
			}
			SoundCaptureDevice captureDevice = SoundCaptureDevice.AllAvailable.Where(d => d.Description == captureDeviceDesc).FirstOrDefault();
			PcmSoundFormat soundFormat = StandartSoundFormats.Where(d => d.Description == soundFormatDesc).FirstOrDefault();

			return new SoundIO(captureDevice, outputType, soundFormat);
			//_mp3SoundCapture = new Mp3SoundCapture
			//{
			//    CaptureDevice = captureDevice,
			//    OutputType = outputType,
			//    WaveFormat = soundFormat,
			//    WaitOnStop = true,
			//    NormalizeVolume = true
			//};
		}
		#region Methods

		/// <summary>
		/// If OutputStream is not null, the recording will begin to that stream.
		/// Else if OutputFilePath is set, the recording will begin to that file.
		/// In other case the recording will not start.
		/// </summary>
		public void StartRecording()
		{
			if (OutputStream != null)
			{
				StartRecording(OutputStream);
				return;
			}

			if (!string.IsNullOrEmpty(OutputFilePath))
			{
				StartRecording(OutputFilePath);
				return;
			}

			throw new ArgumentException("Both OutputStream and OutputFilePath were not set.");
		}

		public void StartRecording(Stream stream)
		{
			_mp3SoundCapture.Start(stream);
		}

		public void StartRecording(string path)
		{
			_mp3SoundCapture.Start(path);
		}


		public void StopRecording()
		{
			_mp3SoundCapture.Stop();
		}
		#endregion

		#region IDisposable
		public void Dispose()
		{
			_mp3SoundCapture.Dispose();
			if (OutputStream != null)
				OutputStream.Close();
		}
		#endregion
	}
}
